/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.bff.engine.core;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.inspur.edp.bef.api.exceptions.ErrorCodes;
import com.inspur.edp.bef.api.lcp.IStandardLcp;
import com.inspur.edp.bef.bizentity.GspBizEntityObject;
import com.inspur.edp.bef.bizentity.GspBusinessEntity;
import com.inspur.edp.bef.component.detailcmpentity.vm.VMComponent;
import com.inspur.edp.bef.component.detailcmpentity.vm.VMMethod;
import com.inspur.edp.bff.api.attribute.AbstractSourceConfig;
import com.inspur.edp.bff.api.dataprocessor.IChangeConvertor;
import com.inspur.edp.bff.api.dataprocessor.IDataConvertor;
import com.inspur.edp.bff.api.dataprocessor.IDefaultValueConvertor;
import com.inspur.edp.bff.api.dataprocessor.IEntityPropertyMapper;
import com.inspur.edp.bff.api.dataprocessor.IFilterFieldConvertor;
import com.inspur.edp.bff.api.extend.IBffEntityExtend;
import com.inspur.edp.bff.api.extend.IBffManagerExtend;
import com.inspur.edp.bff.api.manager.QueryResult;
import com.inspur.edp.bff.api.manager.assembler.IAssemblerManager;
import com.inspur.edp.bff.core.filter.FilterFieldConvertor;
import com.inspur.edp.bff.core.manager.builtinimpl.BffBuiltInManager;
import com.inspur.edp.bff.engine.api.BffEngineManager;
import com.inspur.edp.bff.engine.api.resource.BffEngineModelResInfo;
import com.inspur.edp.bff.engine.core.action.BffEngineAssemblerBase;
import com.inspur.edp.bff.engine.core.action.BffEngineAssemblerManager;
import com.inspur.edp.bff.engine.core.cache.BffEngineCache;
import com.inspur.edp.bff.engine.core.cache.BffEngineCacheService;
import com.inspur.edp.bff.engine.core.response.BffEngineResponseConvertor;
import com.inspur.edp.bff.engine.core.response.BffEngineResponseInfo;
import com.inspur.edp.bff.engine.entity.BffEngineChildData;
import com.inspur.edp.bff.engine.entity.BffEngineRootData;
import com.inspur.edp.bff.engine.entity.EngineViewObjectEntity;
import com.inspur.edp.bff.engine.entity.convertor.BffEngineChangeConvertor;
import com.inspur.edp.bff.engine.entity.convertor.BffEngineDataConvertor;
import com.inspur.edp.bff.engine.entity.defaultvalue.BffEngineDefaultValue;
import com.inspur.edp.bff.engine.entity.defaultvalue.BffEngineDefaultValueMapConvertor;
import com.inspur.edp.bff.engine.entity.serializer.BffEngineRootChangeDeserializer;
import com.inspur.edp.bff.engine.entity.serializer.BffEngineRootChangeSerializer;
import com.inspur.edp.bff.engine.entity.serializer.BffEngineRootDataDeserializer;
import com.inspur.edp.bff.engine.entity.serializer.BffEngineRootDataSerializer;
import com.inspur.edp.bff.spi.AbstractBffEntityDeserConvertor;
import com.inspur.edp.bff.spi.AbstractBffEntitySerConvertor;
import com.inspur.edp.bff.spi.AbstractFSAction;
import com.inspur.edp.bff.spi.IHelpExtend;
import com.inspur.edp.bff.spi.VMHelpConfig;
import com.inspur.edp.bff.spi.action.actionstatck.VoActionContext;
import com.inspur.edp.bff.spi.changeset.AbstractBffChangeJsonDeserializer;
import com.inspur.edp.bff.spi.changeset.AbstractBffChangeJsonSerializer;
import com.inspur.edp.bff.spi.helpextend.VmLinkedConfig;
import com.inspur.edp.bff.spi.helpextend.VmLinkedType;
import com.inspur.edp.bff.spi.request.RequestConvertor;
import com.inspur.edp.bff.spi.response.ResponseConvertor;
import com.inspur.edp.bff.spi.utils.ParameterInfo;
import com.inspur.edp.bff.spi.utils.ReflectInfo;
import com.inspur.edp.cef.api.RefObject;
import com.inspur.edp.cef.api.dataType.base.IAccessorCreator;
import com.inspur.edp.cef.api.dataType.entity.ICefRootEntity;
import com.inspur.edp.cef.api.manager.serialize.CefSerializeContext;
import com.inspur.edp.cef.api.request.RequestInfo;
import com.inspur.edp.cef.api.response.ResponseInfo;
import com.inspur.edp.cef.core.entityaction.CefActionStack;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.spi.entity.resourceInfo.builinImpls.CefEntityResInfoImpl;
import com.inspur.edp.cef.spi.entity.resourceInfo.builinImpls.CefModelResInfoImpl;
import com.inspur.edp.cef.spi.jsonser.base.StringUtils;
import com.inspur.edp.cef.variable.api.manager.IVariableManager;
import com.inspur.edp.commonmodel.engine.api.common.CMEngineUtil;
import com.inspur.edp.commonmodel.engine.core.common.CMUtil;
import com.inspur.edp.das.commonmodel.util.CommonModelExtension;
import com.inspur.edp.formserver.viewmodel.GspViewModel;
import com.inspur.edp.formserver.viewmodel.GspViewObject;
import com.inspur.edp.formserver.viewmodel.action.IViewModelParameter;
import com.inspur.edp.formserver.viewmodel.action.MappedCdpAction;
import com.inspur.edp.formserver.viewmodel.action.ViewModelAction;
import com.inspur.edp.formserver.viewmodel.action.viewmodelbase.ViewModelParameter;
import com.inspur.edp.formserver.viewmodel.collection.VMActionCollection;
import com.inspur.edp.formserver.viewmodel.collection.ValueHelpConfigCollection;
import com.inspur.edp.formserver.viewmodel.common.LinkedHelpConfigUtil;
import com.inspur.edp.formserver.viewmodel.common.ValueHelpConfig;
import com.inspur.edp.formserver.viewmodel.linkedconfig.LinkedConfig;
import com.inspur.edp.formserver.viewmodel.linkedconfig.LinkedType;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.entity.MetadataReference;
import com.inspur.edp.web.help.metadata.HelpMetadataContent;
import io.iec.edp.caf.businessobject.api.entity.DevBasicBoInfo;
import io.iec.edp.caf.businessobject.api.service.DevBasicInfoService;
import io.iec.edp.caf.common.JSONSerializer;
import io.iec.edp.caf.commons.exception.CAFRuntimeException;
import io.iec.edp.caf.commons.exception.ExceptionLevel;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.var;

public class BffEngineManagerImpl extends BffBuiltInManager implements BffEngineManager {

  private GspViewModel vo;
  private GspBusinessEntity be;
  private boolean isVirtual;
  private HashMap<String, String> voNodeMap;
  private HashMap<String, String> beNodeMap;
  private HashMap<String, LinkedHashMap<String, Type>> actionParamTypes = new HashMap<>();
  private Map<String, ViewModelAction> voActions;
  private GspMetadata metadata;

  public BffEngineManagerImpl(String metadataId) {
    super();
    initMetadata(metadataId);
  }

  private void initMetadata(String metadataId) {
    Objects.requireNonNull(metadataId, "metadataId");
    metadata = CMEngineUtil.getMetadata(metadataId);
    Objects.requireNonNull(metadata, "找不到vo元数据, 可以尝试重新打包部署:" + metadataId);
    vo = (GspViewModel) metadata.getContent();
    Objects.requireNonNull(vo, "找不到vo元数据内容, 可以尝试重新打包部署:"+ metadataId);
    // 添加运行时定制扩展
    if(metadata.getHeader().isExtendable()) {
      addExtends();
    }
    initMappingInfo();
  }

  private void initMappingInfo() {
    isVirtual = vo.getIsVirtual();
    if (isVirtual)
      return;
    String beId = vo.getMapping().getTargetMetadataId();
    be = CMEngineUtil.getMetadataContent(beId);
    Objects.requireNonNull(be, "后端元数据包部署失败，vo未找到关联be！ vo元数据id:"+vo.getID()+" name:"+vo.getName()+" code:"+vo.getCode()+", 相关联be元数据id:" + beId);

    voNodeMap = new HashMap<>();
    beNodeMap = new HashMap<>();
    for (var childObj : vo.getAllObjectList()) {
      GspViewObject voChildObj = (GspViewObject) childObj;
      if (voChildObj.isVirtual())
        continue;
      String beObjId = voChildObj.getMapping().getTargetObjId();
      var beChildObj = be.findObjectById(beObjId);
      if (beChildObj == null)
        continue;
      voNodeMap.put(childObj.getCode(), beChildObj.getCode());
      beNodeMap.put(beChildObj.getCode(), childObj.getCode());
    }
  }

  @Override
  protected String getVoId() {
    return vo.getID();
  }

  @Override
  protected IChangeConvertor getChangeMapConvertor() {
    if (isVirtual)
      return null;
    return new BffEngineChangeConvertor(getBffContext(), vo.getMainObject(), be.getMainObject());
  }

  @Override
  protected IDataConvertor getDataConvertor() {
    if (isVirtual)
      return null;
    Object tempVar = be.findObjectById(vo.getMainObject().getMapping().getTargetObjId());
    GspBizEntityObject beObject = (GspBizEntityObject) ((tempVar instanceof GspBizEntityObject) ? tempVar : null);
    return new BffEngineDataConvertor(vo.getMainObject(), beObject);
  }

  @Override
  protected IEntityData innerCreateData() {
    BffEngineRootData data = new BffEngineRootData(vo.getMainObject(),
        (CefEntityResInfoImpl) getCefModelResourceInfo().getCustomResource(vo.getMainObject().getCode()));
    CMUtil.initValue(vo.getMainObject(), data, null,(CefEntityResInfoImpl) getCefModelResourceInfo().getCustomResource(vo.getMainObject().getCode()));
    return data;
  }

  @Override
  protected IEntityData innerCreateChildData(String childCode) {
    GspViewObject childNode = (GspViewObject) CMUtil.findNode(vo, childCode);
    BffEngineChildData data = new BffEngineChildData(childNode,
        (CefEntityResInfoImpl) getCefModelResourceInfo().getCustomResource(childNode.getCode()));
//        data.setID(id);
    CMUtil.initValue(childNode, data, null,(CefEntityResInfoImpl) getCefModelResourceInfo().getCustomResource(childCode));
    return data;
  }

  @Override
  protected AbstractBffEntitySerConvertor getDataJsonSerizlize() {
    return new  BffEngineRootDataSerializer(vo,
        (CefEntityResInfoImpl) ((BffEngineModelResInfo)getModelInfo()).getCustomResource(vo.getMainObject().getCode()));
  }

  @Override
  protected AbstractBffEntityDeserConvertor getDataJsonDeSerizlize() {
    return new BffEngineRootDataDeserializer(vo,
        (CefEntityResInfoImpl) ((BffEngineModelResInfo)getModelInfo()).getCustomResource(vo.getMainObject().getCode()));
  }

  @Override
  protected AbstractBffChangeJsonSerializer getChangeSerializer() {
    return new BffEngineRootChangeSerializer(vo,
        (CefEntityResInfoImpl) ((BffEngineModelResInfo)getModelInfo()).getCustomResource(vo.getMainObject().getCode()));
  }

  @Override
  protected AbstractBffChangeJsonDeserializer getChangeDeserializer() {
    return new BffEngineRootChangeDeserializer(vo,
        (CefEntityResInfoImpl) ((BffEngineModelResInfo)getModelInfo()).getCustomResource(vo.getMainObject().getCode()));
  }

  AbstractBffChangeJsonDeserializer getEngineChangeDeserializer() {
    return getChangeDeserializer();
  }

  @Override
  protected String getBECodeFromVM(String voCode) {
    if (voNodeMap.containsKey(voCode))
      return voNodeMap.get(voCode);
    return null;
  }

  @Override
  protected String getVMCodeFromBE(String beCode) {
    if(beNodeMap.containsKey(beCode))
      return beNodeMap.get(beCode);
    return null;
  }

  @Override
  protected String getHelpId(String s, String s1) {
    Objects.requireNonNull(s);
    Objects.requireNonNull(s1);
    String elementId = s+'/'+s1;
    VMHelpConfig helpConfig = getHelpConfig(s, s1);
    if (helpConfig==null)
      throw new CAFRuntimeException(ErrorCodes.NoScope,"","ID为:"+vo.getId()+",名称为:"+vo.getName()+"的Vo元数据上不存在帮助编号为:"+elementId+"的帮助配置节点。",null, ExceptionLevel.Error);
    String helpId = helpConfig.getHelpId();
    return helpId;
  }

  @Override
  protected ICefRootEntity createVoEntity(String id) {
    return new EngineViewObjectEntity(id, vo.getMainObject(),
        (BffEngineModelResInfo) getCefModelResourceInfo());
  }

  @Override
  public IAccessorCreator getAccessorCreator() {
    return new BffEngineAccessorCreator(vo, getCefModelResourceInfo());
  }

  @Override
  protected IFilterFieldConvertor getFilterConvertor() {
    return new FilterFieldConvertor(this);
  }

  protected IEntityPropertyMapper getRootEntityPropertyMapper() {
    return new BffEnginePropertyMapper(vo,be);
  }

  @Override
  public Class getChangeInfoType() {
    throw new UnsupportedOperationException();
  }

  public Class getRequsetType() {
    return RequestInfo.class;
  }

  @Override
  protected RequestConvertor getRequestConvertor() {

    return new EngineRequestConvertor(this);

  }

  public ResponseInfo createResponse() {
    return new BffEngineResponseInfo();
  }

  @Override
  public IVariableManager createVariableManager() {
    if(vo.getVariables() == null || vo.getVariables().getContainElements().isEmpty()){
      return null;
    } else {
      return new com.inspur.edp.cef.variableengine.core.manager.EngineVariableManager(vo.getVariables());
    }
  }

  @Override
  protected VMHelpConfig getHelpConfig(String codeName, String labelId) {
    Objects.requireNonNull(codeName);
    Objects.requireNonNull(labelId);
    String elementId = codeName+'/'+labelId;

    ValueHelpConfigCollection vmHelpConfigCollection = vo.getValueHelpConfigs();
    for(ValueHelpConfig config: vmHelpConfigCollection){
      if(config == null) {
        continue;
      }
      if(config.getElementId().equals(elementId)){
        return getVmHelpConfig(config,vo.getID(),elementId);
      }
    }
        throw new CAFRuntimeException(ErrorCodes.NoScope,"","VO元数据上"+vo.getName()+"不存在帮助编号为"+elementId+"的节点",null, ExceptionLevel.Error);
  }

  protected VMHelpConfig getVmHelpConfig(ValueHelpConfig config,String voID,String helpElementID){
    VMHelpConfig helpConfig = new VMHelpConfig();
    helpConfig.setHelpId(config.getHelperId());
    helpConfig.setVoID(voID);
    helpConfig.setHelpElementID(helpElementID);
    helpConfig.setFilterCondition(config.getFilterExpression());
    helpConfig.setUseCustomAuthInfo(config.getEnableCustomHelpAuth());
    if(config.getHelpExtend().getBeforeHelp().size() != 0){
      helpConfig.setActionRecord(getActionRecord(config.getHelpExtend().getBeforeHelp()));
    }
    if (config.getLinkedConfigs()!=null && config.getLinkedConfigs().size()>0){
      addVmlinkedConfigs(helpConfig,config,vo);
    }
    //这里没有orderbyCondition
    //helpConfig.setOrderbyCondition(config);
    return helpConfig;
  }

  private void addVmlinkedConfigs(VMHelpConfig helpConfig ,ValueHelpConfig config, GspViewModel vo) {
    List<VmLinkedConfig> vmLinkedConfigs = new ArrayList<>();
    for (LinkedConfig linkedConfig : config.getLinkedConfigs()) {
      VmLinkedConfig vmLinkedConfig = new VmLinkedConfig();
      vmLinkedConfig.setTargetVoId(vo.getID());
      vmLinkedConfig.setSourceFieldInChild(linkedConfig.getSourceFieldInChild());
      vmLinkedConfig.setEnableFilterWhenConditionEmpty(linkedConfig.getEnableFilterWhenConditionEmpty());
      vmLinkedConfig.setLinkHelpType(getVmLinkedType(linkedConfig.getLinkedType()));
      vmLinkedConfig.setSourceObjCode(linkedConfig.getLinkedFieldsMapping().getSourceObjCode());
      handleSourceField(linkedConfig,vmLinkedConfig,linkedConfig.getLinkedFieldsMapping().getSourceObjCode(),vo);
      vmLinkedConfig.setTargetField(linkedConfig.getLinkedFieldsMapping().getTargetField());
      vmLinkedConfig.setTargetObjCode(linkedConfig.getLinkedFieldsMapping().getTargetObjCode());
      vmLinkedConfig.setTargetVoId(getTargetVoId(helpConfig));
      handleIsPathAndTreeField(helpConfig,vmLinkedConfig,linkedConfig);
      vmLinkedConfigs.add(vmLinkedConfig);
    }
    if (vmLinkedConfigs.size() > 0)
      helpConfig.setLinkedConfigs(vmLinkedConfigs);
  }
  private void handleSourceField(LinkedConfig linkedConfig, VmLinkedConfig vmLinkedConfig, String sourceObjCode,GspViewModel vo){
    GspViewObject sourceObject = LinkedHelpConfigUtil.getObjectByCode(vo, sourceObjCode);
    if (sourceObject==null)
        throw new RuntimeException(String.format("在编号为%1$s,名称为%2$S,ID为%3$s的vo元数据上，未获取到编号为%3$s的实体对象",vo.getCode(),vo.getName(),vo.getID(),sourceObjCode));
    String sourceField = linkedConfig.getLinkedFieldsMapping().getSourceField();
    String sourceMappingField = LinkedHelpConfigUtil.getSourceMappingField(sourceObject, sourceField);
    vmLinkedConfig.setSourceField(sourceMappingField);
  }
  private  String getTargetVoId(VMHelpConfig config){
    String helpId = config.getHelpId();
    GspMetadata metadata = CMEngineUtil.getMetadata(helpId);
    Objects.requireNonNull(metadata, "找不到帮助元数据, 需要将要使用的帮助元数据导入或者部署到数据库，帮助元数据ID为" + helpId);
    HelpMetadataContent helpMeta= (HelpMetadataContent) metadata.getContent();
     String targetVoId= helpMeta.getDataSource().getVoSourceId();
    if(targetVoId==null || "".equals(targetVoId))
      throw new RuntimeException(String.format("编号为%1$s，名称为%2$s,ID为%3$s的帮助元数据上记录的VoSourceId为空",helpMeta.getCode(),helpMeta.getName(),helpId));
    return targetVoId;
  }
  private  void handleIsPathAndTreeField(VMHelpConfig helpConfig, VmLinkedConfig vmLinkedConfig, LinkedConfig linkedConfig){
    if (linkedConfig.getLinkedType()==LinkedType.Normal)
      return;
    String helpId = helpConfig.getHelpId();
    GspMetadata metadata = CMEngineUtil.getMetadata(helpId);
    Objects.requireNonNull(metadata, "找不到帮助元数据, 需要将要使用的帮助元数据导入或者部署到数据库，帮助元数据ID为" + helpId);
    HelpMetadataContent helpMeta= (HelpMetadataContent) metadata.getContent();
    boolean pathCodeTree = helpMeta.getDataSource().getTreeInfo().isPathCodeTree();
    String treeField = helpMeta.getDataSource().getTreeInfo().getDataField();
    vmLinkedConfig.setPath(pathCodeTree);
    vmLinkedConfig.setTreeField(treeField);
  }
  private VmLinkedType getVmLinkedType(LinkedType linkedType){
    switch (linkedType){
      case Normal:
        return VmLinkedType.Normal;
      case Tree:
        return VmLinkedType.Tree;
      default:
        throw new RuntimeException("不存在的帮助级联类型"+linkedType);
    }
}
  protected ArrayList<ReflectInfo> getActionRecord(VMActionCollection actionCollection){
    ArrayList<ReflectInfo> actionRecord = new ArrayList<>();
    for(ViewModelAction action: actionCollection){
      if(!(action instanceof MappedCdpAction))
        continue;
      List<ParameterInfo> paraInfoList = new ArrayList<>();
      for(Object parameter: action.getParameterCollection()){
        if(!(parameter instanceof ViewModelParameter))
          continue;
        paraInfoList.add(new ParameterInfo(((ViewModelParameter) parameter).getParamName(),((ViewModelParameter) parameter).getActualValue().getValue()));
      }
      String ComponentEntityId = ((MappedCdpAction)action).getComponentEntityId();
      VMMethod vmMethod = ((VMComponent)CMEngineUtil.getMetadataContent(ComponentEntityId)).getVmMethod();
      ReflectInfo info = new ReflectInfo(vmMethod.getAssembly(),vmMethod.getClassName(),paraInfoList, ComponentEntityId);
      actionRecord.add(info);
    }
    return actionRecord;
  }

  @Override
  protected ArrayList<IHelpExtend> getHelpActions(String codeName, String labelId) {
    switch (codeName + labelId) {
      default:
        return new ArrayList<IHelpExtend>();
    }
  }

  @Override
  public IAssemblerManager getAssemblerManager() {
    return new BffEngineAssemblerManager(vo);
  }

  @Override
  protected IDefaultValueConvertor getDefaultValueConvertor() {
    return new BffEngineDefaultValueMapConvertor(vo.getMainObject(), isVirtual ? null :be.getMainObject());
  }

  @Override
  public Class getDefaultValueType() {
    return BffEngineDefaultValue.class;
  }

  @Override
  public AbstractSourceConfig getSourceConfigs() {
    return new BffEngineSourceConfig(vo, be);
  }

  //region new mode

//    protected void addExtends() {
//        //TODO
//    }

  public String getVoNodeCode(String frontNodeCode) {
    HashMap<String, GspViewObject> nodeDic = vo.getNodesDic();

    for (var item :
        nodeDic.entrySet()) {
      String nodeCode = item.getKey();
      if (nodeCode.equals(frontNodeCode) || StringUtils.toCamelCase(nodeCode).equals(frontNodeCode))
        return nodeCode;
    }
    return null;
  }

  @Override
  public LinkedHashMap<String, Type> getActionParamTypes(String actionCode) {
    if (actionParamTypes.containsKey(actionCode))
      return actionParamTypes.get(actionCode);
    Map<String, ViewModelAction> actions = getAllActions();

    if (!actions.containsKey(actionCode))
      throw new CAFRuntimeException("", "",
          "视图对象【" + vo.getCode() + "】中没有找到自定义动作" + actionCode, null, ExceptionLevel.Error);

    ViewModelAction action = actions.get(actionCode);
    LinkedHashMap<String, Type> paramTypes = new LinkedHashMap<>();
    for (var param :
        action.getParameterCollection()) {
      ViewModelParameter parameter = (ViewModelParameter) param;
      Type paramType = BffEngineAssemblerBase.getNativeTrueType(parameter);
      paramTypes.put(((ViewModelParameter) param).getParamCode(), paramType);
    }
    actionParamTypes.put(actionCode, paramTypes);

    return paramTypes;
  }

  public Object executeCustomAction(String actionCode, LinkedHashMap<String, Object> params) {
    Map<String, ViewModelAction> actions = getAllActions();

    if (!actions.containsKey(actionCode))
      throw new CAFRuntimeException("", "",
          "视图对象【" + vo.getCode() + "】中没有找到自定义动作" + actionCode, null, ExceptionLevel.Error);

    ViewModelAction action = actions.get(actionCode);
    addActParameter(action, params);
    switch (action.getType()) {
      case BEAction:
        VoActionContext voActionContext=new VoActionContext(getContext().getVoId(),action.getID(),actionCode
            ,params);
        CefActionStack.push(voActionContext);
        try {
          IStandardLcp lcp =getBffContext().getLcp();
          final RefObject result = new RefObject(null);
          if(action.getIsAutoSave()) {
            lcp.atomicallyInvoke(() -> {
              result.argvalue = lcp.executeCustomAction(actionCode, params.values().toArray());
              save();
            });
          } else {
            result.argvalue = lcp.executeCustomAction(actionCode, params.values().toArray());
          }
          processResponse();
          return result.argvalue;
        } finally {
          CefActionStack.pop();
        }
      case VMAction:
        AbstractFSAction actionInstance = (AbstractFSAction) BffEngineAssemblerBase.getActionInstance(vo, action, new ArrayList<>(), new ArrayList<>(), params);
        return getBffContext().executeAction(actionInstance, action.getID(), action.getCode(), params, action.getIsAutoSave());//action.getIsAutoSave());
      default:
        throw new CAFRuntimeException("", "",
            "暂不支持类型为【" + action.getType().toString() + "】的动作" + actionCode, null, ExceptionLevel.Error);
    }

  }

  private void addActParameter(ViewModelAction viewModelAction, LinkedHashMap<String, Object> params){
    if(viewModelAction.getParameterCollection() == null || viewModelAction.getParameterCollection().size() == 0)
      return;
    for(int i=0; i< viewModelAction.getParameterCollection().size(); i++){
      IViewModelParameter parameter = viewModelAction.getParameterCollection().getItem(i);
      if(parameter.getActualValue() != null && parameter.getActualValue().getHasValue()){//有实参
        params.put(parameter.getParamCode(), parameter.getActualValue().getValue());
      }
    }
  }

  private Map<String, ViewModelAction> getAllActions() {
    if (voActions == null) {
      voActions = vo.getActions().stream().collect(Collectors.toMap(ViewModelAction::getCode, action -> action));
    }
    return voActions;
  }

  public JsonNode serializeQueryResult(QueryResult result) {
    ObjectMapper mapper = new ObjectMapper();
    ObjectNode node = mapper.createObjectNode();
    ArrayNode array = mapper.createArrayNode();
    for (IEntityData data : result.getResult()) {
      String dataStr = serialize(data);
      try {
        JsonNode dataNode = mapper.readValue(dataStr, JsonNode.class);
        array.add(dataNode);
      } catch (IOException e) {
        throw new RuntimeException("data序列化报错", e);
      }
    }
    node.put("Result", array);
    node.put("Pagination", mapper.valueToTree(result.getPagination()));
    return node;
  }

  @Override
  public boolean isEngine() {
    return true;
  }

  @Override
  public String serializeResponseInfo(ResponseInfo responseInfo, CefSerializeContext context){
    //TODO:copy性能
    ObjectMapper mapper = JSONSerializer.getObjectMapper().copy();
    SimpleModule module = new SimpleModule();
    BffEngineResponseConvertor convertor = new BffEngineResponseConvertor(this);
    setResponseExtList(convertor,this.getContext().getExtList());
    convertor.setContext(context);
    module.addSerializer(BffEngineResponseInfo.class,new BffEngineResponseConvertor(this));
    mapper.registerModule(module);
    try {
      return mapper.writeValueAsString(responseInfo);
    } catch (JsonProcessingException e) {
      throw new RuntimeException("responseInfo序列化报错", e);
    }
  }
  private void setResponseExtList(ResponseConvertor convertor, List<IBffManagerExtend> bffManagerExtends){
    if(bffManagerExtends==null || bffManagerExtends.size()==0)
      return;
    ArrayList<IBffEntityExtend> bffEntityExtends=new ArrayList<>();
    for (IBffManagerExtend managerExtend:bffManagerExtends){
      bffEntityExtends.add(managerExtend.getDataTypeExtend());
    }
    if(bffEntityExtends.size()>0){
      convertor.setExtList(bffEntityExtends);
    }
  }
  public AbstractBffChangeJsonSerializer getEngineChangeSerializer(){
    return getChangeSerializer();
  }
  //endregion

  public AbstractBffEntitySerConvertor getEngineEntityDataSerializer(){
    return getDataJsonSerizlize();
  }
  @Override
  public CefModelResInfoImpl getCefModelResourceInfo() {
    BffEngineCache cache;
    if(metadata!=null && metadata.getProperties()!=null && metadata.getProperties().getCacheVersion()!=null && !"".equals(metadata.getProperties().getCacheVersion())) {
      cache = BffEngineCacheService.getBefEngineInfo(vo.getID(), metadata.getProperties().getCacheVersion());
    }else {
      cache = BffEngineCacheService.getBefEngineInfo(vo.getID());
    }

    if(cache.getModelResInfo()!=null)
      return cache.getModelResInfo();

    String resMetaDataId = "";
    for(MetadataReference reference:metadata.getRefs()){
      if(reference.getDependentMetadata().getType().equals("ResourceMetadata")){
        resMetaDataId = reference.getDependentMetadata().getId();
      break;
      }
    }

    String su= CommonModelExtension.getSUCode(vo);
    if(StringUtils.isEmpty(su)) {
      DevBasicBoInfo devBasicBoInfo = SpringBeanUtils.getBean(DevBasicInfoService.class)
          .getDevBasicBoInfo(metadata.getHeader().getBizobjectID());
      if(devBasicBoInfo == null) {
        throw new RuntimeException(
            "ID为[" + vo.getId() + "] 编号为[" + vo.getCode() + "] 名称为[" + vo.getName()
                + "]的业务实体所属业务对象[" + metadata.getHeader().getBizobjectID() + "]不存在");
      }
      su=devBasicBoInfo.getSuCode();
      CommonModelExtension.setSUCode(vo, su);
    }
    BffEngineModelResInfo resInfo= new BffEngineModelResInfo(vo,su,resMetaDataId,be);
    cache.setModelResInfo(resInfo);
    return resInfo;
  }
}
